//#define __COMPILE_DEBUG__

#include "zepto.h"
#include "internal.h"

struct zepto builtin_fns[] = {
  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "COND",
      .eval_data.arg_count = 0,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = IGNORE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = COND
    },
  },
  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "CONS",
      .eval_data.arg_count = 2,
      .eval_data.eval_mask = 3,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = CONS
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "DEFINE",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = HAVE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = DEFINE
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "LAMBDA",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = HAVE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = LAMBDA
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "SET!",
      .eval_data.arg_count = 2,
      .eval_data.eval_mask = 2,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = SET
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "QUOTE",
      .eval_data.arg_count = 0,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = IGNORE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = QUOTE
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "LET",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = HAVE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = LET
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "CAR",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 1,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = SYM_CAR
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "CDR",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 1,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = SYM_CDR
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "SET-CAR!",
      .eval_data.arg_count = 2,
      .eval_data.eval_mask = 3,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = SET_CAR
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "SET-CDR!",
      .eval_data.arg_count = 2,
      .eval_data.eval_mask = 3,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = SET_CDR
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "BEGIN",
      .eval_data.arg_count = 0,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = HAVE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = BEGIN
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "LIST",
      .eval_data.arg_count = 0,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = IGNORE_TAIL,
      .eval_data.eval_tail = EVAL_TAIL,
      .code.builtin.func_num = LIST
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "NULL?",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 1,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = NULLP
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "PAIR?",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 1,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = PAIRP
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "NUMBER?",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 1,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = NUMBERP
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "ATOM?",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 1,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = ATOMP
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "STRING?",
      .eval_data.arg_count = 1,
      .eval_data.eval_mask = 1,
      .eval_data.have_tail = NO_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = STRINGP
    },
  },
    { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "AND",
      .eval_data.arg_count = 0,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = HAVE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = AND
    },
  },

  { .type = ZEPTO_BUILTIN,
    .data.function = &(struct function) {
      .code.builtin.name = "OR",
      .eval_data.arg_count = 0,
      .eval_data.eval_mask = 0,
      .eval_data.have_tail = HAVE_TAIL,
      .eval_data.eval_tail = NO_EVAL_TAIL,
      .code.builtin.func_num = OR
    },
  },
};

#define BUILTINS_NUM (sizeof(builtin_fns) / sizeof(builtin_fns[0]))

// comparison function
static int fn_comp(const void *a, const void *b)
{
  return ((struct zepto *)a)->data.function->code.builtin.func_num -
    ((struct zepto *)b)->data.function->code.builtin.func_num;
}

void _export_foreign(char *sym, struct zepto *fn)
{
  bool new;
  struct zepto *symbol = sym_intern(sym, &new);

  if(!new) {
    ERROR("Foreign function name used twice: \"%s\"", sym);
  }
  
  env_define(&toplevel_env, symbol, fn);
}

void add_builtins(void)
{
  int i;
  struct zepto *sym;
  bool new;
  
  // sort builtin functions.
  // actually it is not needed as they are already sorted
  // but if anybody adds new function and misposition it
  // in builtins array this will lead to interpreter
  // misbehavior, so sort a small array once is not too
  // much for it.

  DEBUG("sorting builtins");

  DEBUG("--%lu %lu %lu %lu--", sizeof(struct zepto), sizeof(*builtin_fns), sizeof(builtin_fns[0]), sizeof(builtin_fns));
  
  qsort(builtin_fns, BUILTINS_NUM, sizeof(*builtin_fns), fn_comp);

  for(i = 0; i < BUILTINS_NUM; i++) {
    DEBUG("defining %s", builtin_fns[i].data.function->code.builtin.name);
    sym = sym_intern(builtin_fns[i].data.function->code.builtin.name, &new);
    env_define(&toplevel_env, sym, &builtin_fns[i]);
    DEBUG("defined");
  }

  CQUOTE = sym_lookup("QUOTE");
  ASSERT(CQUOTE);

  register_io();
  register_math();
  register_logic();
  register_equality();
  register_string();

  DEBUG("Complete.");
}
  
